#include "crossbit/RBThread.h"
#include <iostream>
#include "string.h"
#include "crossbit/SYSCALL.h"

using namespace crossbit;

//#define RB_DEBUG

RBFuncIndx RBFuncTable[RBFUNCNUM] = {
	{4,"clone",(XTMemAddr)(RBThreadManager::RBThreadCreate)},
	{1,"RB_createlock",(XTMemAddr)(RBThreadManager::RBCreateLock)},
	{1,"RB_lock",(XTMemAddr)(RBThreadManager::RBLock)},
	{1,"RB_unlock",(XTMemAddr)(RBThreadManager::RBUnLock)},
	{1,"exit",(XTMemAddr)(RBThreadManager::RBThreadExit)}
};


RBThreadManager::RBThreadManager(MemoryImage *rb_mi, ProcessorState *rb_ps):rb_memory_image(rb_mi),rb_processor_state(rb_ps)
{
	Thread mainthread;
	ThreadId thread_id = (ThreadId) ++GLOBAL::THREAD_TOTAL;
	mainthread.thread_id = thread_id;
	mainthread.state = running;
	thread_tbl.insert(std::pair<ThreadId, Thread>((ThreadId) thread_id, mainthread));

	thread_running = thread_id;
	thread_block = 0;
}



//void RBThreadManager::RBThreadCreate(RBThreadManager *_this, XTInt32 *thread_id, void *attr, void * (start_routine)(void*), void *arg)
void RBThreadManager::RBThreadCreate(RBThreadManager *_this, int (*start_routine)(void *), void *child_stack, int flags, void *arg) 
{
	//std::cout<<"RBThreadCreate is invoked!!"<<std::endl;

	//printf("sp of newthread is 0x%x\n", (XTInt32) *(stk_top+38));
	Thread newthread; 
	newthread.regs[1] = (XTMemAddr) _this->rb_processor_state->reg(1); //$at
	newthread.regs[25] = (XTMemAddr) start_routine; //t9
	newthread.regs[28] = (XTMemAddr) _this->rb_processor_state->reg(28); //gp
	newthread.regs[29] = (XTMemAddr) child_stack; //sp
	newthread.regs[30] = (XTMemAddr) child_stack; //fp
	newthread.regs[31] = (XTMemAddr) GLOBAL::EXIT_ADDR; //ra

	ThreadId thread_id = (ThreadId) ++GLOBAL::THREAD_TOTAL;
	newthread.thread_id = thread_id;
	newthread.next_SPC = (XTMemAddr) start_routine;
	newthread.state = idle;
	_this->thread_tbl.insert(std::pair<ThreadId, Thread>((ThreadId) thread_id, newthread));

	// set return value
	_this->rb_processor_state->put(2, thread_id);

	//_this->RBSchedule();
}

void RBThreadManager::RBCreateLock(RBThreadManager *_this, int *li)
{	

#ifdef RB_DEBUG
	printf("RBCreateLock!\n");
#endif

	LockId lock_id = (LockId) li; //++GLOBAL::LOCK_TOTAL;
	Lock newlock;
	newlock.lock_type = mutex;
	newlock.lock_id = lock_id;
	newlock.owner_id = 0;

	_this->lock_tbl.insert(std::pair<LockId, Lock>(lock_id, newlock));

}

void RBThreadManager::RBLock(RBThreadManager *_this, int lock_id)
{

#ifdef RB_DEBUG
	printf("RBLock in thread_%d!\n", _this->thread_running);
					std::cout << "lock_id_1 is " << lock_id << std::endl
										<< "lock_id_2 is " << _this->lock_tbl.find(lock_id)->second.lock_id << std::endl;
#endif
	//lock_id = 268453976;
	std::map<LockId, Lock>::iterator ll_iter = _this->lock_tbl.find(lock_id);

	if(ll_iter == _this->lock_tbl.end())
	{
					//std::cout << "lock_tbl size is " << _this->lock_tbl.size() << std::endl;
					std::cout << "Lock wasn't created!" << std::endl;
					exit(1);
	}

	Lock lock = ll_iter->second;
	if(lock.owner_id == 0 || lock.owner_id == _this->thread_running)		//This lock is available
	{
					//std::cout << "this lock is available!" << std::endl;
					//update the state of the lock in lock_tbl
					_this->lock_tbl.erase(ll_iter);
					lock.owner_id = _this->thread_running;
					_this->lock_tbl.insert(std::pair<LockId, Lock>(lock.lock_id, lock));
	}
	else																																//if this lock has been owned by other thread
	{
					//std::cout << "this lock is owned by other thread!" << std::endl;
					//update the state of the current thread.
					std::map<ThreadId, Thread>::iterator tt_iter = _this->thread_tbl.find(_this->thread_running);
					Thread thread = tt_iter->second;
					if(tt_iter == _this->thread_tbl.end())
					{
									std::cout << "The owner of Lock_" << thread.thread_id << " doesn't exist!" << std::endl;
					}

					_this->thread_tbl.erase(tt_iter);
					thread.state = block;
					thread.blockBy = lock.lock_id;
					_this->thread_tbl.insert(std::pair<ThreadId, Thread>(thread.thread_id, thread));
					_this->thread_block = _this->thread_running;
					
	}

#ifdef RB_DEBUG
	printf("RBLock complete in thread_%d!\n", _this->thread_running);
#endif

}

void RBThreadManager::RBUnLock(RBThreadManager *_this, int lock_id)
{

#ifdef RB_DEBUG
	printf("RBUnLock in thread_%d!\n", _this->thread_running);
					std::cout << "lock_id_1 is " << lock_id << std::endl
										<< "lock_id_2 is " << _this->lock_tbl.find(lock_id)->second.lock_id << std::endl;
#endif

	std::map<LockId, Lock>::iterator ll_iter = _this->lock_tbl.find(lock_id);
	Lock lock = ll_iter->second;

	if(ll_iter == _this->lock_tbl.end())
	{
					std::cout << "Lock wasn't created!" << std::endl;
					exit(1);
	}
	if(lock.owner_id != _this->thread_running)
	{
					std::cout << "The thread_" << _this->thread_running << " should be block now!" << std::endl;
					exit(1);
	}
  if(lock.owner_id == 0)
	{
					std::cout << "The Lock_" << lock.lock_id << " has been released!" << std::endl;
	}

	_this->lock_tbl.erase(lock_id);
	lock.owner_id = 0;
	_this->lock_tbl.insert(std::pair<LockId, Lock>(lock.lock_id, lock));

#ifdef RB_DEBUG
	printf("RBUnLock complete in thread_%d!\n", _this->thread_running);
#endif

}

void RBThreadManager::RBThreadCleanupPush(RBThreadManager *_this, void * (routine)(void*), void *arg)
{
	std::cout<<"RBThreadCleanupPush is invoked!!"<<std::endl;
}

void RBThreadManager::RBThreadJoin(RBThreadManager *_this, XTInt32 thread_id, void **status)
{
	std::cout<<"RBThreadJoin is invoked!!"<<std::endl;
}

void RBThreadManager::RBThreadExit(RBThreadManager *_this, void *status)
{

#ifdef RB_DEBUG
	printf("Exit is invoked by thread_%d\n", _this->thread_running);
#endif

	_this->thread_tbl.erase(_this->thread_running);
	if(_this->thread_tbl.empty())
	{
					exit(1);
	}
	_this->thread_running = 0;
}

XTMemAddr RBThreadManager::RBSchedule(XTMemAddr nextSPC, XTMemAddr entry)
{		

	std::map<ThreadId, Thread>::iterator tt_iter;
	ThreadId thread_to_id = pickout();
	
	if(thread_to_id == thread_running)
	{
					return nextSPC;
	}

	tt_iter = thread_tbl.find(thread_running);
	//normal
	if(0 == thread_block && tt_iter != thread_tbl.end())
	{
		Thread thread_yield = tt_iter->second; 
		thread_tbl.erase(tt_iter);
		memcpy(&(thread_yield.regs), (void*)rb_processor_state->get(0), 4*34);
		memcpy(&(thread_yield.fregs), (void*)rb_processor_state->fpGet(0), 4*34);	
		thread_yield.next_SPC = nextSPC;	
		thread_yield.state = idle;
		thread_tbl.insert(std::pair<ThreadId, Thread>(thread_yield.thread_id, thread_yield));
	}

	//if end from a block thread 
	if(0 != thread_block)
	{
		tt_iter = thread_tbl.find(thread_running);
		if(tt_iter == thread_tbl.end())
		{
						SYSCALL::XTExit(-1);
		}

		Thread thread = tt_iter->second;
		thread_tbl.erase(tt_iter);
		thread.next_SPC = entry;
		thread_tbl.insert(std::pair<ThreadId, Thread>(thread.thread_id, thread));
		thread_block = 0;

	}

	tt_iter = thread_tbl.find(thread_to_id);
	if(tt_iter == thread_tbl.end())
	{
					SYSCALL::XTExit(-1);	
	}
	Thread thread_to = tt_iter->second;
	thread_tbl.erase(tt_iter);

	memcpy((void*)rb_processor_state->get(0), &(thread_to.regs), 4*34);
	memcpy((void*)rb_processor_state->fpGet(0), &(thread_to.fregs), 4*34);	
	thread_to.state = running; 
	thread_tbl.insert(std::pair<ThreadId, Thread>(thread_to.thread_id, thread_to));

	thread_running = thread_to_id;


	return thread_to.next_SPC;

}

ThreadId RBThreadManager::pickout()
{
	XTUint32 thread_tbl_size = thread_tbl.size();
	
	XTUint32 random = rand()%thread_tbl_size;	
	std::map<ThreadId, Thread>::iterator tt_iter = thread_tbl.begin();
	for(int i = 0; i < random; i++)
		tt_iter++;

	while(tt_iter->second.state == block) 
	{
					//if the lock that block this thread is still owned by other thread
					if(lock_tbl.find(tt_iter->second.blockBy)->second.owner_id != 0)		
									tt_iter++;
					//if the lock that block this thread has been released
					else	
									break;
	}

	return tt_iter->first;	
}

